import React from "react";
import ReactDOM from "react-dom";
import ModalConatiner from "./index";

type GetContainer = string | Element | (() => (Element | null)) | null | undefined;
type Children<P extends {} > = string | React.FunctionComponent<P> | React.ComponentClass<P> | React.FC<P>

type ModalContatinerOption = {
  destroyTime?: number,
  delayTime?: number,
  singleton?: boolean,
}
type ModalContatinerInstanceOption = {
  getContainer: GetContainer,
  className: string,
  style: string,
  replace: boolean,
}
export interface ModalContatinerDefaultOption<T> {
  visible?: boolean,
  onClose?: (arg0: T) => void,
}


const getContainerFunc = (getContainer?: GetContainer): Element => {
  let container = getContainer;
  if (typeof getContainer === 'function') container = getContainer()
  if (typeof getContainer === 'string') container = document.querySelector(getContainer)
  if (container instanceof Element) return container
  else return document.body
}

export const asyncModalWarpper = <T extends ModalContatinerDefaultOption<F> = {}, F = {}>(
  children: Children<T>,
  { destroyTime = 2000, delayTime = 0, singleton = false }: ModalContatinerOption = {}
) => {
  const component = children;
  let flag: Promise<F> | null = null;
  const render = (properties: T, modalParams?: Partial<ModalContatinerInstanceOption>) => {
    const { getContainer, className, style, replace = false } = modalParams || {}
    const container = getContainerFunc(getContainer)
    const ele = <ModalConatiner>{React.createElement(component, properties)}</ModalConatiner>
    let div = document.createElement("div");
    className && div.setAttribute("class", className)
    style && div.setAttribute("style", style)
    !replace && container.appendChild(div)
    ReactDOM.render(ele, replace ? container : div)
    return {
      destroy() {
        !replace && container.removeChild(div)
        ReactDOM.unmountComponentAtNode(div)
      }
    };
  };
  const showModal = async (params: T, modalParams?: Partial<ModalContatinerInstanceOption>) => {
    // 单例模式不再继续new
    if (singleton && flag) return flag;
    flag = new Promise((resolve) => {
      const onClose = (data: F) => {
        resolve(data);
        setTimeout(() => destroyInstance(), destroyTime);
      };
      let instance = render({ onClose, visible: delayTime ? false : true, ...params }, modalParams);
      if (delayTime) {
        setTimeout(() => {
          instance && instance.destroy();
          instance = render({ onClose, visible: true, ...params }, modalParams);
        }, delayTime);
      }
      const destroyInstance = () => instance && instance.destroy();
    });
    flag.finally(() => {
      flag = null;
    });
    return flag;
  };
  return showModal;
};
